1 module hip.api.net.utils; 2 import std.traits; 3 4 5 extern(C) @safe pure @nogc 6 { 7 ushort htons(ushort x); 8 uint htonl(uint x); 9 ushort ntohs(ushort x); 10 uint ntohl(uint x); 11 } 12 13 T hton(T)(T data) @nogc nothrow pure 14 { 15 version(WebAssembly) 16 { 17 return *(cast(T*)toNetworkBytes(data).ptr); 18 } 19 else 20 { 21 static if(T.sizeof == 2) 22 return htons(data); 23 else static if(T.sizeof == 4) 24 return htonl(data); 25 } 26 } 27 28 size_t sizeofTypes(T...)() @nogc nothrow pure if(!hasDynamicArray!T) 29 { 30 size_t ret; 31 foreach (t; T) 32 ret+= t.sizeof; 33 return ret; 34 } 35 36 bool hasDynamicArray(T)() 37 { 38 static if(is(T == struct)) 39 { 40 foreach(v; T.init.tupleof) 41 { 42 static if(hasDynamicArray!(typeof(v))) 43 return true; 44 } 45 return false; 46 } 47 else 48 return isDynamicArray!T; 49 } 50 51 /** 52 * Used for determining if the data can be copied to stack and sent. This is the most efficient way to send data 53 * since it won't allocate on frame. 54 * Returns: If the type sequence has a dynamic array 55 */ 56 bool hasDynamicArray(T...)() 57 { 58 static foreach(t; T) 59 { 60 static if(hasDynamicArray!t) 61 return true; 62 } 63 return false; 64 } 65 66 @nogc nothrow pure 67 ubyte[sizeofTypes!T] toBytes(T...)(T input) if(!hasDynamicArray!(T)) 68 { 69 typeof(return) ret = void; 70 size_t offset; 71 static foreach(i, t; T) 72 { 73 ret[offset..offset+t.sizeof] = toBytes!(t, t.sizeof)(input[i]); 74 offset+= t.sizeof; 75 } 76 return ret; 77 } 78 79 void copyIntoMemory(T)(T struc, ubyte[] memory) 80 { 81 static if(is(T == struct) && hasDynamicArray!T) 82 { 83 uint sz; 84 foreach(v; struc.tupleof) 85 { 86 uint tSize = getSendTypeSize(v); 87 copyIntoMemory(v, memory[sz..sz+tSize]); 88 sz+= tSize; 89 } 90 } 91 else static if(isDynamicArray!T) 92 { 93 memory[0..uint.sizeof] = toBytes(cast(uint)struc.length); 94 95 static if(hasDynamicArray!(typeof(T.init[0]))) 96 { 97 size_t offset = uint.sizeof; 98 foreach(i; 0..struc.length) 99 { 100 size_t sz = getSendTypeSize(struc[i]); 101 copyIntoMemory(struc[i], memory[offset..offset+sz]); 102 offset+= sz; 103 } 104 } 105 else 106 { 107 size_t sz = struc.length * T.init[0].sizeof; 108 memory[uint.sizeof..uint.sizeof+sz] = (cast(ubyte*)struc.ptr)[0..sz]; 109 } 110 } 111 else 112 { 113 memory[] = toBytes(struc); 114 } 115 } 116 117 ubyte[] toBytes(T...)(T input) if(hasDynamicArray!T) 118 { 119 import std.stdio; 120 121 ubyte[] ret; 122 size_t offset; 123 foreach(i, t; T) 124 { 125 size_t sz = getSendTypeSize(input[i]); 126 ret.length+= sz; 127 copyIntoMemory(input[i], ret[offset..offset+sz]); 128 offset+= sz; 129 } 130 return ret; 131 } 132 133 uint getSendTypeSize(T...)(const T input) if(T.length > 1) 134 { 135 uint sz; 136 foreach(i, t; T) 137 { 138 sz+= getSendTypeSize(input[i]); 139 } 140 return sz; 141 } 142 143 uint getSendTypeSize(T)(const T input = T.init) if(!is(T == struct) && !isDynamicArray!T) 144 { 145 return T.sizeof; 146 } 147 uint getSendTypeSize(T)(const T input = T.init) if(isDynamicArray!T) 148 { 149 uint sz = uint.sizeof; 150 static if(hasDynamicArray!(T)) 151 { 152 foreach(v; input) 153 { 154 sz+= getSendTypeSize(v); 155 } 156 } 157 else 158 { 159 sz+= T.init[0].sizeof * input.length; 160 } 161 return sz; 162 } 163 164 uint getSendTypeSize(T)(const T input = T.init) if(is(T == struct)) 165 { 166 uint sz; 167 foreach(v; input.tupleof) 168 { 169 static if(hasDynamicArray!(typeof(v))) 170 { 171 sz+= getSendTypeSize(v); 172 } 173 else 174 { 175 sz+= typeof(v).sizeof; 176 } 177 } 178 return sz; 179 } 180 181 ubyte[N] toBytes(T, uint N = T.sizeof)(T input) @nogc nothrow pure 182 { 183 ubyte[N] ret = void; 184 ret[] = (cast(ubyte*)&input)[0..N]; 185 return ret; 186 } 187 188 bool isLittleEndian() @nogc nothrow pure 189 { 190 uint value = 1; 191 return (cast(ubyte*)&value)[0] == 1; 192 } 193 194 ubyte[N] swapEndian(uint N)(ubyte[N] bytes) @nogc nothrow pure 195 { 196 ubyte[N] swapped = void; 197 foreach (i; 0 .. N) 198 swapped[i] = bytes[N - 1 - i]; 199 return swapped; 200 } 201 202 ubyte[] swapEndian(const ubyte[] bytes) 203 { 204 ubyte[] swapped; 205 swapped.length = bytes.length; 206 foreach (i; 0 .. bytes.length) 207 swapped[i] = bytes[bytes.length - 1 - i]; 208 return swapped; 209 } 210 211 void swapEndianInPlace(ref ubyte[] bytes) @nogc nothrow pure 212 { 213 foreach (i; 0 .. bytes.length / 2) 214 { 215 auto tmp = bytes[i]; 216 bytes[i] = bytes[bytes.length - 1 - i]; 217 bytes[bytes.length - 1 - i] = tmp; 218 } 219 } 220 221 ubyte[] toNetworkBytes(ubyte[] input) 222 { 223 return isLittleEndian ? swapEndian(input) : input; 224 } 225 226 ubyte[N] toNetworkBytes(T, uint N = T.sizeof)(T input) @nogc nothrow pure 227 { 228 ubyte[N] ret = toBytes(input); 229 return isLittleEndian ? swapEndian(ret) : ret; 230 } 231 232 ///Same implementation, must go back from big to little or kepe it as it is 233 alias fromNetworkBytes = toNetworkBytes; 234 235 236 void toNetworkBytesInPlace(ref ubyte[] bytes) @nogc nothrow pure 237 { 238 if(isLittleEndian) 239 swapEndianInPlace(bytes); 240 } 241 242 alias fromNetworkBytesInPlace = toNetworkBytesInPlace; 243 244 245 246 247 ubyte[N] toNetwork(T, uint N = T.sizeof)(T data) @nogc nothrow pure 248 { 249 ubyte[N] ret; 250 static foreach(mem; __traits(allMembers, T)) 251 {{ 252 alias member = __traits(getMember, T, mem); 253 254 static if(member.sizeof == 1) 255 ret[member.offsetof] = cast(ubyte)__traits(child, data, member); 256 else static if( 257 is(typeof(member) == uint) || 258 is(typeof(member) == ushort) || 259 is(typeof(member) == int) || 260 is(typeof(member) == short) 261 ) 262 ret[member.offsetof..member.offsetof + member.sizeof] = toBytes(hton(__traits(child, data, member))); 263 else 264 ret[member.offsetof..member.offsetof + member.sizeof] = toNetworkBytes(__traits(child, data, member)); 265 }} 266 return ret; 267 } 268 269 270 unittest 271 { 272 struct Ultra 273 { 274 int[] a; 275 } 276 277 struct EvenTester 278 { 279 Ultra[] tester; 280 } 281 assert(hasDynamicArray!Ultra); 282 Ultra b; 283 b.a~= [500, 200]; 284 285 EvenTester t; 286 t.tester~= b; 287 288 // assert(getSendTypeSize(b) == 12); 289 // import hip.api.net.hipnet; 290 291 // assert(getSendTypeSize(NetHeader.init, b) == 17); 292 // assert(toBytes(NetHeader.init, b).length == 17); 293 294 import std.stdio; 295 296 // writeln = getSendTypeSize(t.tester); 297 } 298 299 unittest 300 { 301 import hip.api.net.server; 302 import hip.api.net.controller; 303 import hip.api.net.hipnet; 304 305 ConnectedClientsResponse resp; 306 307 ConnectedClient c = ConnectedClient(NetConnectInfo(NetIPAddress(null, 0, IPType.ipv4), 0)); 308 resp.clients~= [c]; 309 310 ubyte[] sendData = getNetworkFormattedData(resp, MarkedNetReservedTypes.get_connected_clients); 311 ubyte[] respData = fromNetworkBytes(sendData); 312 313 assert(getSendTypeSize(c) == 11); 314 315 316 respData = respData[NetHeader.sizeof..$-1]; 317 assert(respData.length == 15); 318 319 auto interpreted = getNetworkStruct!(ConnectedClientsResponse)(respData); 320 321 // writeln = interpreted.type; 322 assert(interpreted == resp); 323 }